<template>
  <div class="file-info-panel">
    <!-- 顶部卡片：大按钮，适合触摸 -->
    <a-card class="file-card-top">
      <div class="file-top-row">
        <a-upload
          :before-upload="beforeUpload"
          :show-upload-list="false"
          accept=".stl"
          @change="handleChange"
        >
          <a-button type="primary" :disabled="!!fileName" class="big-btn" size="large">
            <UploadOutlined />
          </a-button>
        </a-upload>
        <a-space warp>
          <a-button
            type="primary"
            danger
            :disabled="!fileName"
            @click="showModal"
            class="big-btn"
            size="large"
          >
            <DeleteOutlined />
          </a-button>
        </a-space>
        <span v-if="fileName" class="file-name">{{ fileName }}</span>
      </div>
      <a-modal
        v-model:open="open"
        title="确认删除"
        @ok="hideModal"
        @cancel="cancelModal"
        ok-text="确认"
        cancel-text="取消"
      >
        确定要删除该文件吗？
      </a-modal>
    </a-card>

    <!-- 中部卡片：三维预览，适配触摸 -->
    <a-card class="file-card-middle">
      <div v-if="fileBase64" class="stl-viewer-area">
        <div ref="stlContainer" class="stl-canvas"></div>
      </div>
      <div v-else class="stl-viewer-placeholder">图像展示区域</div>
    </a-card>

    <!-- 底部卡片：占位 -->
    <a-card class="file-card-bottom">
      <div class="bottom-placeholder">底部占位</div>
    </a-card>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, onUnmounted, nextTick, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useFileStore } from '@/stores/file'
import { message } from 'ant-design-vue'
import * as THREE from 'three'
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { UploadOutlined, DeleteOutlined } from '@ant-design/icons-vue'

const fileStore = useFileStore()
const { fileName, fileBase64 } = storeToRefs(fileStore)
const stlContainer = ref<HTMLDivElement | null>(null)
let renderer: THREE.WebGLRenderer | null = null
let scene: THREE.Scene | null = null
let camera: THREE.PerspectiveCamera | null = null
let animationId: number | null = null
let controls: OrbitControls | null = null

const open = ref(false)
function showModal() {
  open.value = true
}
function hideModal() {
  open.value = false
  handleDelete()
}
function cancelModal() {
  open.value = false
}

function beforeUpload(file: File) {
  const isStl = file.name.endsWith('.stl')
  if (!isStl) {
    message.error('只能上传STL文件！')
  }
  return isStl
}

function handleChange(info: { file: { originFileObj: File } }) {
  const file = info.file.originFileObj
  if (file) {
    const reader = new FileReader()
    reader.onload = (e) => {
      const base64 = e.target?.result as string
      fileStore.setFile(file.name, base64)
    }
    reader.readAsDataURL(file)
  }
}

function handleDelete() {
  fileStore.clearFile()
  cleanupThree()
  message.success('文件已删除')
}

function renderSTL(url: string) {
  if (!stlContainer.value) return
  cleanupThree()
  scene = new THREE.Scene()
  camera = new THREE.PerspectiveCamera(
    75,
    stlContainer.value.clientWidth / stlContainer.value.clientHeight,
    0.1,
    1000,
  )
  camera.position.set(0, 0, 100)
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
  renderer.setSize(stlContainer.value.clientWidth, stlContainer.value.clientHeight)
  stlContainer.value.appendChild(renderer.domElement)
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.08
  controls.screenSpacePanning = false
  controls.minDistance = 10
  controls.maxDistance = 500
  controls.target.set(0, 0, 0)
  controls.update()
  const light = new THREE.DirectionalLight(0xffffff, 1)
  light.position.set(1, 1, 1).normalize()
  scene.add(light)
  const loader = new STLLoader()
  loader.load(url, (geometry: THREE.BufferGeometry) => {
    const material = new THREE.MeshPhongMaterial({ color: 0x108ee9 })
    const mesh = new THREE.Mesh(geometry, material)
    geometry.computeBoundingBox()
    if (geometry.boundingBox) {
      const center = geometry.boundingBox.getCenter(new THREE.Vector3())
      mesh.position.sub(center)
    }
    scene!.add(mesh)
    animate()
  })
}

function animate() {
  if (!renderer || !scene || !camera) return
  animationId = requestAnimationFrame(animate)
  if (controls) controls.update()
  renderer.render(scene, camera)
}

function cleanupThree() {
  if (animationId) cancelAnimationFrame(animationId)
  if (controls) {
    controls.dispose()
    controls = null
  }
  if (renderer) {
    renderer.dispose()
    renderer = null
  }
  scene = null
  camera = null
  if (stlContainer.value) stlContainer.value.innerHTML = ''
}

// 关键：监听 base64，变化时自动渲染
watch(fileBase64, async (newBase64) => {
  if (newBase64) {
    await nextTick()
    // base64 转 blob url
    const arr = newBase64.split(',')
    const mime = arr[0].match(/:(.*?);/)[1]
    const bstr = atob(arr[1])
    let n = bstr.length
    const u8arr = new Uint8Array(n)
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    const blob = new Blob([u8arr], { type: mime })
    const url = URL.createObjectURL(blob)
    renderSTL(url)
  } else {
    cleanupThree()
  }
})

onMounted(async () => {
  if (fileBase64.value) {
    await nextTick()
    const arr = fileBase64.value.split(',')
    const mime = arr[0].match(/:(.*?);/)[1]
    const bstr = atob(arr[1])
    let n = bstr.length
    const u8arr = new Uint8Array(n)
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    const blob = new Blob([u8arr], { type: mime })
    const url = URL.createObjectURL(blob)
    renderSTL(url)
  }
})

onUnmounted(() => {
  cleanupThree()
})
</script>

<style scoped>
.file-info-panel {
  display: flex;
  flex-direction: column;
  height: 100vh;
  gap: 10px;
  font-size: 1.4rem;
}
.file-card-top,
.file-card-bottom {
  min-height: 80px;
  max-height: 120px;
  height: 100px;
  display: flex;
  align-items: center;
}
.file-card-middle {
  flex: 1 1 auto;
  min-width: 700px;
  min-height: 320px;
  max-height: 70vh;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.file-top-row {
  display: flex;
  align-items: center;
  gap: 32px;
}
.big-btn {
  font-size: 1.6rem !important;
  height: 56px !important;
  min-width: 160px !important;
  border-radius: 16px !important;
  margin-right: 8px;
}
.file-name {
  font-size: 1.3rem;
  color: #222;
  margin-left: 24px;
  word-break: break-all;
  max-width: 320px;
  display: inline-block;
}
.stl-viewer-area {
  width: 100%;
  height: 100%;
  min-height: 320px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.stl-canvas {
  width: 700px;
  height: 500px;
  background: #f5f6fa;
  border-radius: 12px;
  box-shadow: 0 2px 8px #eee;
  touch-action: none; /* 允许触摸交互 */
}
.stl-viewer-placeholder {
  width: 700px;
  height: 400px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #bbb;
  font-size: 1.5rem;
  background: #f5f6fa;
  border-radius: 12px;
}
.bottom-placeholder {
  text-align: center;
  color: #ccc;
  font-size: 1.2rem;
}
</style>
